Finishing protocol fixes

This commit is contained in:
Jaime Freire 2024-01-03 00:36:15 +01:00
parent 965636544e
commit d29d2e53bd
6 changed files with 121 additions and 89 deletions

View File

@ -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<ActorRef<ParticipantMessage>> participants;
private int totalTurns;
private final ParticipantType participantType;
private Participant(
ActorContext<ParticipantMessage> 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<ParticipantMessage> 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<ParticipantMessage> 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<ParticipantMessage> onSessionDenied(
ParticipantProtocol.SessionDenied message) {
SessionDenied message) {
context.getLog().info("cannot start chat room session: {}", message.reason());
return Behaviors.stopped();
}
private Behavior<ParticipantMessage> onSessionGranted(
ParticipantProtocol.SessionGranted message) {
SessionGranted message) {
setChatRoom(message.chatRoom());
setSession(message.session());
return Behaviors.same();
}
private Behavior<ParticipantMessage> 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<SessionMessage> 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<ParticipantMessage> 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<SessionProtocol.ParticipateInTurn> 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);
}
}

View File

@ -31,6 +31,9 @@ public class ParticipantProtocol {
public record SessionDenied(String reason) implements ParticipantMessage {
}
public record SessionEnded() implements ParticipantMessage {
}
public record PotReturned(
ActorRef<SessionMessage> session,
ActorRef<ParticipantMessage> participant,

View File

@ -26,11 +26,10 @@ public class PotRoom {
private PotRoom(
ActorContext<PotRoomMessage> context,
List<ActorRef<ParticipantMessage>> 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<ParticipantMessage> participant = enterPot.replyTo();
ActorRef<SessionMessage> 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<SessionMessage> 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<PotRoomMessage> create(int numberOfParticipants, int turns) {
public static Behavior<PotRoomMessage> 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()));
}
}

View File

@ -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<SessionMessage> context;
private int currentTurn = 0;
private final int totalTurns;
private final int numberOfParticipants;
private final List<ActorRef<ParticipantMessage>> participants;
private double currentPot = 0.0;
private int participantsInCurrentTurn;
private int numberOfParticipantsInCurrentTurn;
public Session(ActorContext<SessionMessage> context, int totalTurns, int numberOfParticipants) {
public Session(ActorContext<SessionMessage> context, List<ActorRef<ParticipantMessage>> 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<SessionMessage> 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<SessionMessage> create(int numberOfParticipants, int turns) {
return Behaviors.setup(context -> new Session(context, numberOfParticipants, turns)
public static Behavior<SessionMessage> create(List<ActorRef<ParticipantMessage>> participants, int totalTurns) {
return Behaviors.setup(context -> new Session(context, participants, totalTurns)
.createSessionBehaviour());
}
private Behavior<SessionMessage> 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<SessionMessage> onSessionEnded(List<ActorRef<ParticipantMessage>> participants) {
participants.forEach(participant -> participant.tell(new SessionEnded()));
return Behaviors.stopped();
}
private static Behavior<SessionMessage> onSessionStarted(
ActorRef<PotRoomMessage> chatRoom,
ActorRef<SessionMessage> session,
List<ActorRef<ParticipantMessage>> 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<SessionMessage> onSharePotWithParticipant(
private static Behavior<SessionMessage> onReturnPotToParticipants(
ActorRef<SessionMessage> session,
ActorRef<ParticipantMessage> participant,
List<ActorRef<ParticipantMessage>> participants,
double returnedAmount) {
participant.tell(new ParticipantProtocol.PotReturned(session, participant, returnedAmount));
participants.forEach(participant -> participant.tell(new PotReturned(session, participant, returnedAmount)));
return Behaviors.same();
}

View File

@ -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<SessionMessage> session,
ActorRef<ParticipantMessage> participant,
List<ActorRef<ParticipantMessage>> participants,
double returnedAmount) implements SessionMessage {
}
public record EndSession() implements SessionMessage { }
public record PlayTurn(
ActorRef<SessionMessage> session,
ActorRef<ParticipantMessage> replyTo,
List<ActorRef<ParticipantMessage>> participants,
int turn,
double pot)
implements SessionMessage {

View File

@ -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<ParticipantMessage> p3 =
testKit.spawn(Participant.create(INITIAL_COINS, SANTO), "SANTO-1");
ActorRef<SessionMessage> sessionP1 =
testKit.spawn(Session.create(TOTAL_PARTICIPANTS, 1), encode(p1.path().name(), UTF_8));
ActorRef<SessionMessage> sessionP2 =
testKit.spawn(Session.create(TOTAL_PARTICIPANTS, 1), encode(p2.path().name(), UTF_8));
ActorRef<SessionMessage> sessionP3 =
testKit.spawn(Session.create(TOTAL_PARTICIPANTS, 1), encode(p3.path().name(), UTF_8));
final List<ActorRef<SessionMessage>> 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));
}