diff --git a/src/main/java/dev/freireservices/social_altruism/chat/participant/Participant.java b/src/main/java/dev/freireservices/social_altruism/chat/participant/Participant.java index f730681..8c11a58 100644 --- a/src/main/java/dev/freireservices/social_altruism/chat/participant/Participant.java +++ b/src/main/java/dev/freireservices/social_altruism/chat/participant/Participant.java @@ -7,10 +7,13 @@ import akka.actor.typed.ActorRef; import akka.actor.typed.Behavior; import akka.actor.typed.javadsl.ActorContext; import akka.actor.typed.javadsl.Behaviors; -import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.ParticipantMessage; +import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.*; import dev.freireservices.social_altruism.chat.potroom.PotRoomProtocol.PotRoomMessage; import java.security.SecureRandom; +import java.sql.Time; +import java.util.List; +import java.util.concurrent.TimeUnit; import dev.freireservices.social_altruism.chat.potroom.SessionProtocol; import dev.freireservices.social_altruism.chat.potroom.SessionProtocol.SessionMessage; @@ -27,19 +30,19 @@ public class Participant { private boolean collaborateSwitch; private int currentTurn; - private double coins; + private double participantCoins; private final double initialCoins; - private int participantNumber; + private List> participants; private int totalTurns; private final ParticipantType participantType; private Participant( ActorContext context, - double coins, + double participantCoins, ParticipantType participantType) { this.context = context; - this.coins = coins; - this.initialCoins = coins; + this.participantCoins = participantCoins; + this.initialCoins = participantCoins; this.participantType = participantType; this.collaborateSwitch = participantType == JUSTICIERO || participantType == SANTO; } @@ -50,39 +53,51 @@ public class Participant { } public void decrementCoins(double coins) { - this.coins -= coins; + this.participantCoins -= coins; } public void incrementCoins(double coins) { - this.coins += coins; + this.participantCoins += coins; } private Behavior behavior() { return Behaviors.receive(ParticipantMessage.class) - .onMessage(ParticipantProtocol.SessionStarted.class, this::onSessionStarted) - .onMessage(ParticipantProtocol.SessionDenied.class, this::onSessionDenied) - .onMessage(ParticipantProtocol.SessionGranted.class, this::onSessionGranted) - .onMessage(ParticipantProtocol.PotReturned.class, this::onPotReturned) + .onMessage(SessionStarted.class, this::onSessionStarted) + .onMessage(SessionDenied.class, this::onSessionDenied) + .onMessage(SessionGranted.class, this::onSessionGranted) + .onMessage(PotReturned.class, this::onPotReturned) + .onMessage(SessionEnded.class, this::onSessionEnded) .build(); } + private Behavior onSessionEnded(SessionEnded sessionEnded) { + + context.getLog().info("Session ended for user {}", context.getSelf().path().name()); + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return Behaviors.stopped(); + } + private Behavior onSessionDenied( - ParticipantProtocol.SessionDenied message) { + SessionDenied message) { context.getLog().info("cannot start chat room session: {}", message.reason()); return Behaviors.stopped(); } private Behavior onSessionGranted( - ParticipantProtocol.SessionGranted message) { + SessionGranted message) { setChatRoom(message.chatRoom()); setSession(message.session()); return Behaviors.same(); } private Behavior onSessionStarted( - ParticipantProtocol.SessionStarted startSession) { + SessionStarted startSession) { resetCurrentTurn(); - setParticipantNumber(startSession.participants().size()); + setParticipants(startSession.participants()); setTotalTurns(startSession.totalTurns()); playTurn(startSession.replyTo()); context.getLog().info("Session started for {} with {} participants", context.getSelf().path().name(), startSession.participants().size()); @@ -90,19 +105,19 @@ public class Participant { } private void playTurn(ActorRef replyTo) { - if (getCoins() > 0 && getCurrentTurn() < getTotalTurns()) { + if (getParticipantCoins() > 0 && getCurrentTurn() < getTotalTurns()) { replyTo.tell(new SessionProtocol.PlayTurn( replyTo, context.getSelf(), + participants, getCurrentTurn(), getParticipationForCurrentTurn() )); - incrementCurrentTurn(); } } private double getParticipationForCurrentTurn() { - var currentTurnCoins = getRandomNumberBetween(0, Math.floor(getCoins())); + var currentTurnCoins = getRandomNumberBetween(0, Math.floor(getParticipantCoins())); decrementCoins(currentTurnCoins); return currentTurnCoins; } @@ -113,21 +128,26 @@ public class Participant { } private Behavior onPotReturned( - ParticipantProtocol.PotReturned potReturned) { - context.getLog().info("Pot returned: {}", potReturned.returnedAmount()); + PotReturned potReturned) { + context.getLog().info("Pot returned: {} for participant {}", potReturned.returnedAmount(), potReturned.participant().path().name()); incrementCoins(potReturned.returnedAmount()); - + incrementCurrentTurn(); context .getLog() .info( - "Player {} has now {} coins; started with {} for a total profit of: {} %", + "Player {} has now {} coins; started with {} for a partial profit of: {} %", potReturned.participant().path().name(), - getCoins(), + getParticipantCoins(), getInitialCoins(), calculateProfit()); // Still game? - if (getCoins() > 0) { + if (getParticipantCoins() > 1) { + try { + TimeUnit.SECONDS.sleep(5); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } playTurn(potReturned.session()); } else { context @@ -135,17 +155,20 @@ public class Participant { .info( "Player {} has now {} coins; started with {} for a total profit of: {} %", potReturned.participant().path().name(), - getCoins(), + getParticipantCoins(), getInitialCoins(), calculateProfit()); + context.getLog().info("END GAME"); + context.getLog().info("---------"); + context.getLog().info("END GAME"); return Behaviors.stopped(); } adjustBehaviour(potReturned); return Behaviors.same(); } - private void adjustBehaviour(ParticipantProtocol.PotReturned potReturned) { + private void adjustBehaviour(PotReturned potReturned) { switch (participantType) { case SANTO: @@ -156,25 +179,20 @@ public class Participant { break; case JUSTICIERO: // Tweak minimum amount to collaborate - setCollaborateSwitch(potReturned.returnedAmount() > getParticipantNumber()); + setCollaborateSwitch(potReturned.returnedAmount() > participants.size()); break; } } - public void participant() { - ActorRef handle; - // return this; - } - public void resetCurrentTurn() { setCurrentTurn(0); } public void incrementCurrentTurn() { - setCurrentTurn(this.currentTurn++); + setCurrentTurn(++this.currentTurn); } private double calculateProfit() { - return Math.round((getCoins() * 100) / getInitialCoins() - 100); + return Math.round((getParticipantCoins() * 100) / getInitialCoins() - 100); } } diff --git a/src/main/java/dev/freireservices/social_altruism/chat/participant/ParticipantProtocol.java b/src/main/java/dev/freireservices/social_altruism/chat/participant/ParticipantProtocol.java index 5a56c6a..51181d0 100644 --- a/src/main/java/dev/freireservices/social_altruism/chat/participant/ParticipantProtocol.java +++ b/src/main/java/dev/freireservices/social_altruism/chat/participant/ParticipantProtocol.java @@ -31,6 +31,9 @@ public class ParticipantProtocol { public record SessionDenied(String reason) implements ParticipantMessage { } + public record SessionEnded() implements ParticipantMessage { + } + public record PotReturned( ActorRef session, ActorRef participant, diff --git a/src/main/java/dev/freireservices/social_altruism/chat/potroom/PotRoom.java b/src/main/java/dev/freireservices/social_altruism/chat/potroom/PotRoom.java index 14884fe..3ef1727 100644 --- a/src/main/java/dev/freireservices/social_altruism/chat/potroom/PotRoom.java +++ b/src/main/java/dev/freireservices/social_altruism/chat/potroom/PotRoom.java @@ -26,11 +26,10 @@ public class PotRoom { private PotRoom( ActorContext context, - List> participants, int numberOfParticipants, int totalTurns) { this.context = context; - this.participants = participants; + this.participants = new ArrayList<>(); this.numberOfParticipants = numberOfParticipants; this.totalTurns = totalTurns; } @@ -44,25 +43,25 @@ public class PotRoom { ActorRef participant = enterPot.replyTo(); - ActorRef session = - context.spawn( - Session.create(getNumberOfParticipants(), totalTurns), - URLEncoder.encode(enterPot.replyTo().path().name(), UTF_8)); - - participant.tell(new SessionGranted(chatRoom, session.narrow())); - participants.add(participant); if (getNumberOfParticipants() == participants.size()) { context.getLog().info("All participants joined; pot is ready to start."); + ActorRef session = + context.spawn( + Session.create(participants, totalTurns), + URLEncoder.encode(enterPot.replyTo().path().name(), UTF_8)); + + participant.tell(new SessionGranted(chatRoom, session.narrow())); + // Communicate session start and share pot info with all participants participants.forEach(p -> p.tell(new SessionStarted(chatRoom, session, participants, totalTurns))); return createPotBehaviour(chatRoom); } else { // Waiting for more participants - context.getLog().info("Waiting for more participants."); + context.getLog().info("Waiting for {} more participant(s).", numberOfParticipants - participants.size()); return Behaviors.same(); } } @@ -87,10 +86,8 @@ public class PotRoom { .build(); } - public static Behavior create(int numberOfParticipants, int turns) { + public static Behavior create(int numberOfParticipants, int totalTurns) { return Behaviors.setup( - ctx -> - new PotRoom(ctx, new ArrayList<>(), numberOfParticipants, turns) - .createPotBehaviour(ctx.getSelf())); + ctx -> new PotRoom(ctx, numberOfParticipants, totalTurns).createPotBehaviour(ctx.getSelf())); } } diff --git a/src/main/java/dev/freireservices/social_altruism/chat/potroom/Session.java b/src/main/java/dev/freireservices/social_altruism/chat/potroom/Session.java index 063287a..75cce7b 100644 --- a/src/main/java/dev/freireservices/social_altruism/chat/potroom/Session.java +++ b/src/main/java/dev/freireservices/social_altruism/chat/potroom/Session.java @@ -6,11 +6,15 @@ import akka.actor.typed.javadsl.ActorContext; import akka.actor.typed.javadsl.Behaviors; import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol; import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.ParticipantMessage; +import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.PotReturned; +import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.SessionEnded; +import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.SessionStarted; import dev.freireservices.social_altruism.chat.potroom.PotRoomProtocol.PotRoomMessage; -import dev.freireservices.social_altruism.chat.potroom.SessionProtocol.SessionMessage; +import dev.freireservices.social_altruism.chat.potroom.SessionProtocol.*; import lombok.Getter; import java.util.List; +import java.util.concurrent.TimeUnit; @Getter public class Session { @@ -18,16 +22,16 @@ public class Session { private final ActorContext context; private int currentTurn = 0; private final int totalTurns; - private final int numberOfParticipants; + private final List> participants; private double currentPot = 0.0; - private int participantsInCurrentTurn; + private int numberOfParticipantsInCurrentTurn; - public Session(ActorContext context, int totalTurns, int numberOfParticipants) { + public Session(ActorContext context, List> participants, int totalTurns) { this.context = context; + this.participants = participants; this.totalTurns = totalTurns; - this.numberOfParticipants = numberOfParticipants; } @@ -40,11 +44,11 @@ public class Session { } public void incrementParticipantsInTurn() { - this.participantsInCurrentTurn++; + this.numberOfParticipantsInCurrentTurn++; } - public void resetParticipantsInTurn() { - this.participantsInCurrentTurn = 0; + public void resetNumberOfParticipantsInTurn() { + this.numberOfParticipantsInCurrentTurn = 0; } public int incrementCurrentTurnAndGet() { @@ -54,23 +58,24 @@ public class Session { public Behavior createSessionBehaviour() { return Behaviors.receive(SessionMessage.class) - .onMessage(SessionProtocol.StartSession.class, startSession -> onSessionStarted(startSession.chatRoom() + .onMessage(StartSession.class, startSession -> onSessionStarted(startSession.chatRoom() , startSession.replyTo(), startSession.participants(), totalTurns)) - .onMessage(SessionProtocol.PlayTurn.class, this::onPlayTurn) - .onMessage(SessionProtocol.ShareReturnPotWithParticipants.class, - sharePot -> onSharePotWithParticipant(sharePot.session(), sharePot.participant(), sharePot.returnedAmount())) + .onMessage(PlayTurn.class, this::onPlayTurn) + .onMessage(EndSession.class, endSession -> onSessionEnded(participants)) + .onMessage(ShareReturnPotWithParticipants.class, + sharePot -> onReturnPotToParticipants(sharePot.session(), sharePot.participants(), sharePot.returnedAmount())) .build(); } - public static Behavior create(int numberOfParticipants, int turns) { - return Behaviors.setup(context -> new Session(context, numberOfParticipants, turns) + public static Behavior create(List> participants, int totalTurns) { + return Behaviors.setup(context -> new Session(context, participants, totalTurns) .createSessionBehaviour()); } private Behavior onPlayTurn( - SessionProtocol.PlayTurn playTurn) { + PlayTurn playTurn) { context.getLog() .info("Participant {} joined for turn {} with {}", playTurn.replyTo().path().name(), @@ -81,39 +86,51 @@ public class Session { addToPot(playTurn.pot()); incrementParticipantsInTurn(); - if (getParticipantsInCurrentTurn() == numberOfParticipants) { + if (getNumberOfParticipantsInCurrentTurn() == participants.size()) { - double amountToShare = (getCurrentPot() * 2) / numberOfParticipants; + double amountToShare = (getCurrentPot() * 2) / participants.size(); - playTurn.session().narrow().tell(new SessionProtocol.ShareReturnPotWithParticipants(playTurn.session(), playTurn.replyTo(), amountToShare)); + playTurn.session().narrow().tell(new ShareReturnPotWithParticipants(playTurn.session(), playTurn.participants(), amountToShare)); resetPot(); - resetParticipantsInTurn(); + resetNumberOfParticipantsInTurn(); context.getLog().info("Turn {} complete", getCurrentTurn()); if (incrementCurrentTurnAndGet() == totalTurns) { context.getLog().info("All turns completed"); - //return Behaviors.stopped(); + context.getLog().info("Waiting for other messages, then ending session."); + try { + TimeUnit.SECONDS.sleep(5); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + playTurn.session().narrow().tell(new EndSession()); + } } return Behaviors.same(); } + private Behavior onSessionEnded(List> participants) { + participants.forEach(participant -> participant.tell(new SessionEnded())); + return Behaviors.stopped(); + } + private static Behavior onSessionStarted( ActorRef chatRoom, ActorRef session, List> participants, int totalTurns) { - participants.forEach(s -> s.tell(new ParticipantProtocol.SessionStarted(chatRoom, session, participants, totalTurns))); + participants.forEach(s -> s.tell(new SessionStarted(chatRoom, session, participants, totalTurns))); return Behaviors.same(); } - private static Behavior onSharePotWithParticipant( + private static Behavior onReturnPotToParticipants( ActorRef session, - ActorRef participant, + List> participants, double returnedAmount) { - participant.tell(new ParticipantProtocol.PotReturned(session, participant, returnedAmount)); + participants.forEach(participant -> participant.tell(new PotReturned(session, participant, returnedAmount))); return Behaviors.same(); } diff --git a/src/main/java/dev/freireservices/social_altruism/chat/potroom/SessionProtocol.java b/src/main/java/dev/freireservices/social_altruism/chat/potroom/SessionProtocol.java index 84b6be9..bb36790 100644 --- a/src/main/java/dev/freireservices/social_altruism/chat/potroom/SessionProtocol.java +++ b/src/main/java/dev/freireservices/social_altruism/chat/potroom/SessionProtocol.java @@ -1,6 +1,7 @@ package dev.freireservices.social_altruism.chat.potroom; import akka.actor.typed.ActorRef; +import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol; import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.ParticipantMessage; import dev.freireservices.social_altruism.chat.potroom.PotRoomProtocol.PotRoomMessage; import lombok.Getter; @@ -20,18 +21,21 @@ public class SessionProtocol { implements SessionMessage { } - public record ParticipateInTurn(String message) implements SessionMessage { - } - public record ShareReturnPotWithParticipants( ActorRef session, - ActorRef participant, + List> participants, double returnedAmount) implements SessionMessage { } + + public record EndSession() implements SessionMessage { } + + + public record PlayTurn( ActorRef session, ActorRef replyTo, + List> participants, int turn, double pot) implements SessionMessage { diff --git a/src/test/java/dev/freireservices/social_altruism/chat/PotQuickStartTest.java b/src/test/java/dev/freireservices/social_altruism/chat/PotQuickStartTest.java index 19c8fe3..87cef0e 100644 --- a/src/test/java/dev/freireservices/social_altruism/chat/PotQuickStartTest.java +++ b/src/test/java/dev/freireservices/social_altruism/chat/PotQuickStartTest.java @@ -10,6 +10,7 @@ import akka.actor.testkit.typed.javadsl.TestProbe; import akka.actor.typed.ActorRef; import akka.actor.typed.javadsl.ActorContext; import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.ParticipantMessage; +import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.SessionEnded; import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.SessionStarted; import dev.freireservices.social_altruism.chat.potroom.PotRoomProtocol; import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol; @@ -20,6 +21,7 @@ import dev.freireservices.social_altruism.chat.potroom.Session; import java.time.Duration; import java.util.List; +import dev.freireservices.social_altruism.chat.potroom.SessionProtocol; import dev.freireservices.social_altruism.chat.potroom.SessionProtocol.SessionMessage; import org.junit.Test; @@ -48,24 +50,15 @@ public class PotQuickStartTest { ActorRef p3 = testKit.spawn(Participant.create(INITIAL_COINS, SANTO), "SANTO-1"); - - ActorRef sessionP1 = - testKit.spawn(Session.create(TOTAL_PARTICIPANTS, 1), encode(p1.path().name(), UTF_8)); - ActorRef sessionP2 = - testKit.spawn(Session.create(TOTAL_PARTICIPANTS, 1), encode(p2.path().name(), UTF_8)); - ActorRef sessionP3 = - testKit.spawn(Session.create(TOTAL_PARTICIPANTS, 1), encode(p3.path().name(), UTF_8)); - - final List> sessions = List.of(sessionP1, sessionP2, sessionP3); - // Enter POT chatRoomTest.tell(new PotRoomProtocol.EnterPot(p1)); chatRoomTest.tell(new PotRoomProtocol.EnterPot(p2)); - //chatRoomTest.tell(new PotRoomProtocol.EnterPot(p3)); + chatRoomTest.tell(new PotRoomProtocol.EnterPot(p3)); + //chatRoomTest.tell(new PotRoomProtocol.EnterPot(testProbe.ref())); //Session started - testProbe.expectMessageClass(SessionStarted.class, Duration.ofSeconds(10)); + testProbe.expectMessageClass(SessionEnded.class, Duration.ofSeconds(20)); }