Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
fbe95c4c04 | ||
|
a2c2e9faf6 | ||
|
c8a7f22a45 | ||
|
4abcfc2178 | ||
|
70f9ad4d99 | ||
|
d29d2e53bd | ||
|
965636544e | ||
|
12b7224510 | ||
|
1d6505a2ca | ||
|
6cb6de28fc | ||
|
76e1781864 | ||
|
3b223dfea2 |
183
.gitignore
vendored
Normal file
183
.gitignore
vendored
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
/target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/git,java,maven,eclipse,windows
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
|
||||||
|
.metadata
|
||||||
|
bin/
|
||||||
|
tmp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
.recommenders
|
||||||
|
|
||||||
|
# External tool builders
|
||||||
|
.externalToolBuilders/
|
||||||
|
|
||||||
|
# Locally stored "Eclipse launch configurations"
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# PyDev specific (Python IDE for Eclipse)
|
||||||
|
*.pydevproject
|
||||||
|
|
||||||
|
# CDT-specific (C/C++ Development Tooling)
|
||||||
|
.cproject
|
||||||
|
|
||||||
|
# CDT- autotools
|
||||||
|
.autotools
|
||||||
|
|
||||||
|
# PDT-specific (PHP Development Tools)
|
||||||
|
.buildpath
|
||||||
|
|
||||||
|
# sbteclipse plugin
|
||||||
|
.target
|
||||||
|
|
||||||
|
# Tern plugin
|
||||||
|
.tern-project
|
||||||
|
|
||||||
|
# TeXlipse plugin
|
||||||
|
.texlipse
|
||||||
|
|
||||||
|
# Code Recommenders
|
||||||
|
.recommenders/
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated/
|
||||||
|
|
||||||
|
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||||
|
.cache-main
|
||||||
|
.scala_dependencies
|
||||||
|
.worksheet
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
|
||||||
|
.sts4-cache/
|
||||||
|
|
||||||
|
### Git ###
|
||||||
|
# Created by git for backups. To disable backups in Git:
|
||||||
|
# $ git config --global mergetool.keepBackup false
|
||||||
|
*.orig
|
||||||
|
|
||||||
|
# Created by git when using merge tools for conflicts
|
||||||
|
*.BACKUP.*
|
||||||
|
*.BASE.*
|
||||||
|
*.LOCAL.*
|
||||||
|
*.REMOTE.*
|
||||||
|
*_BACKUP_*.txt
|
||||||
|
*_BASE_*.txt
|
||||||
|
*_LOCAL_*.txt
|
||||||
|
*_REMOTE_*.txt
|
||||||
|
|
||||||
|
### Java ###
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
### Maven ###
|
||||||
|
target/
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### Windows ###
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
/build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Some additional ignores (sort later)
|
||||||
|
*.DS_Store
|
||||||
|
*.sw?
|
||||||
|
.#*
|
||||||
|
*#
|
||||||
|
*~
|
||||||
|
bin
|
||||||
|
build
|
||||||
|
target
|
||||||
|
*.sublime-*
|
||||||
|
/scratch
|
||||||
|
.gradle
|
||||||
|
README.html
|
||||||
|
.exercism
|
||||||
|
/target/
|
||||||
|
/.idea/
|
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
15
.idea/compiler.xml
generated
Normal file
15
.idea/compiler.xml
generated
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<annotationProcessing>
|
||||||
|
<profile default="true" name="Default" enabled="true" />
|
||||||
|
<profile name="Maven default annotation processors profile" enabled="true">
|
||||||
|
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||||
|
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||||
|
<outputRelativeToContentRoot value="true" />
|
||||||
|
<module name="social_altruism" />
|
||||||
|
</profile>
|
||||||
|
</annotationProcessing>
|
||||||
|
<bytecodeTargetLevel target="19" />
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/google-java-format.xml
generated
Normal file
6
.idea/google-java-format.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GoogleJavaFormatSettings">
|
||||||
|
<option name="enabled" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
17
.idea/gradle.xml
generated
Normal file
17
.idea/gradle.xml
generated
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="gradleJvm" value="corretto-19" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
35
.idea/jarRepositories.xml
generated
Normal file
35
.idea/jarRepositories.xml
generated
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RemoteRepositoriesConfiguration">
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Central Repository" />
|
||||||
|
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="MavenRepo" />
|
||||||
|
<option name="name" value="MavenRepo" />
|
||||||
|
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Maven Central repository" />
|
||||||
|
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="jboss.community" />
|
||||||
|
<option name="name" value="JBoss Community repository" />
|
||||||
|
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="MavenLocal" />
|
||||||
|
<option name="name" value="MavenLocal" />
|
||||||
|
<option name="url" value="file:$MAVEN_REPOSITORY$/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="akka-repository" />
|
||||||
|
<option name="name" value="Akka library repository" />
|
||||||
|
<option name="url" value="https://repo.akka.io/maven" />
|
||||||
|
</remote-repository>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/jpa-buddy.xml
generated
Normal file
6
.idea/jpa-buddy.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JpaBuddyIdeaProjectConfig">
|
||||||
|
<option name="renamerInitialized" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
16
.idea/misc.xml
generated
Normal file
16
.idea/misc.xml
generated
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="MavenProjectsManager">
|
||||||
|
<option name="originalFiles">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/pom.xml" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="workspaceImportForciblyTurnedOn" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="corretto-19" project-jdk-type="JavaSDK" />
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="jpab" />
|
||||||
|
</component>
|
||||||
|
</project>
|
124
.idea/uiDesigner.xml
generated
Normal file
124
.idea/uiDesigner.xml
generated
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Palette2">
|
||||||
|
<group name="Swing">
|
||||||
|
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="Button" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="RadioButton" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="CheckBox" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="Label" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||||
|
<preferred-size width="200" height="200" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||||
|
<preferred-size width="200" height="200" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||||
|
<preferred-size width="-1" height="20" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||||
|
</item>
|
||||||
|
</group>
|
||||||
|
</component>
|
||||||
|
</project>
|
7
.idea/vcs.xml
generated
Normal file
7
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
23
README.txt
Normal file
23
README.txt
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
Juego del bien público
|
||||||
|
----------------------
|
||||||
|
- Varios jugadores.
|
||||||
|
- Reciben N monedas al comienzo.
|
||||||
|
- Hay un bote central.
|
||||||
|
|
||||||
|
- Cada turno se elige con cuántas monedas uno colabora al bote.
|
||||||
|
- Entonces, el contenido del bote se multiplica por dos y se divide entre todos los jugadores.
|
||||||
|
- Pasadas N rondas, el jugador se queda con lo que ganó + monedas iniciales.
|
||||||
|
|
||||||
|
- Tipos de jugadores:
|
||||||
|
- Pícaro: no colabora nunca.
|
||||||
|
- Santo: colabora siempre.
|
||||||
|
- Justiciero: colabora si y solo si todos los jugadores han colaborado en el turno anterior.
|
||||||
|
|
||||||
|
Comprobar distribución de ganancia según variación en tipo de jugadores; Pícaro, santo o justiciero.
|
||||||
|
|
||||||
|
10 jugadores, 100 turnos, 100 monedas iniciales.
|
||||||
|
|
||||||
|
|
||||||
|
Comprobar cómo de dispuesta está la gente a contribuir a un bien, que será compartido por todos por igual,
|
||||||
|
hayan contribuido o no.
|
122
pom.xml
Normal file
122
pom.xml
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
<!-- #build-sample -->
|
||||||
|
<project>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>hello-akka-java</groupId>
|
||||||
|
<artifactId>SocialAltruism</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<akka.version>2.9.0</akka.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>akka-repository</id>
|
||||||
|
<name>Akka library repository</name>
|
||||||
|
<url>https://repo.akka.io/maven</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-reload4j -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-reload4j</artifactId>
|
||||||
|
<version>2.0.9</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.typesafe.akka</groupId>
|
||||||
|
<artifactId>akka-actor-typed_2.13</artifactId>
|
||||||
|
<version>${akka.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.typesafe.akka</groupId>
|
||||||
|
<artifactId>akka-actor-testkit-typed_2.13</artifactId>
|
||||||
|
<version>${akka.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.13.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility</artifactId>
|
||||||
|
<version>4.2.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility-proxy</artifactId>
|
||||||
|
<version>3.1.6</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.hamcrest/hamcrest -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hamcrest</groupId>
|
||||||
|
<artifactId>hamcrest</artifactId>
|
||||||
|
<version>2.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<version>5.8.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.bytebuddy</groupId>
|
||||||
|
<artifactId>byte-buddy</artifactId>
|
||||||
|
<version>1.14.11</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.30</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.11.0</version>
|
||||||
|
<configuration>
|
||||||
|
<source>19</source>
|
||||||
|
<target>19</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
<configuration>
|
||||||
|
<executable>java</executable>
|
||||||
|
<arguments>
|
||||||
|
<argument>-classpath</argument>
|
||||||
|
<classpath />
|
||||||
|
<argument>dev.freireservices.social_altruism.chat.PotQuickStart</argument>
|
||||||
|
</arguments>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
@ -0,0 +1,84 @@
|
|||||||
|
package dev.freireservices.social_altruism.chat;
|
||||||
|
|
||||||
|
import akka.actor.typed.ActorRef;
|
||||||
|
import akka.actor.typed.ActorSystem;
|
||||||
|
import akka.actor.typed.Behavior;
|
||||||
|
import akka.actor.typed.Terminated;
|
||||||
|
import akka.actor.typed.javadsl.Behaviors;
|
||||||
|
import dev.freireservices.social_altruism.chat.participant.Participant;
|
||||||
|
import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol;
|
||||||
|
import dev.freireservices.social_altruism.chat.potroom.PotRoom;
|
||||||
|
import dev.freireservices.social_altruism.chat.potroom.PotRoomProtocol;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static dev.freireservices.social_altruism.chat.participant.ParticipantType.*;
|
||||||
|
|
||||||
|
public class PotQuickStart {
|
||||||
|
|
||||||
|
public static final int MONEDAS_INIT = 100;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// #actor-system
|
||||||
|
ActorSystem.create(Main.create(), "PotRoom");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Main {
|
||||||
|
|
||||||
|
static List<ActorRef<ParticipantProtocol.ParticipantMessage>> sessions = new ArrayList<>();
|
||||||
|
|
||||||
|
static final int numberOfParticipants = 3;
|
||||||
|
static final int numberOfTurns = 100;
|
||||||
|
|
||||||
|
public static Behavior<Void> create() {
|
||||||
|
return Behaviors.setup(
|
||||||
|
context -> {
|
||||||
|
ActorRef<PotRoomProtocol.PotRoomMessage> chatRoom =
|
||||||
|
context.spawn(
|
||||||
|
PotRoom.create(numberOfParticipants, numberOfTurns), "potRoom");
|
||||||
|
|
||||||
|
// Agregamos jugadores
|
||||||
|
final var picaroZero = Participant.create(MONEDAS_INIT, PICARO);
|
||||||
|
final var santoZero = Participant.create(MONEDAS_INIT, SANTO);
|
||||||
|
final var justiZero = Participant.create(MONEDAS_INIT, JUSTICIERO);
|
||||||
|
|
||||||
|
sessions.add(context.spawn(picaroZero, "participante-picaro-0"));
|
||||||
|
sessions.add(context.spawn(santoZero, "participante-santo-0"));
|
||||||
|
sessions.add(context.spawn(justiZero, "participante-justiciero-0"));
|
||||||
|
|
||||||
|
// Entrar en pot
|
||||||
|
for (ActorRef<ParticipantProtocol.ParticipantMessage> session : sessions) {
|
||||||
|
chatRoom.tell(new PotRoomProtocol.EnterPot(session));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for (int i = 0; i < numberOfTurns; i++) {
|
||||||
|
// Participar en cada turno cantidad diferente
|
||||||
|
for (ActorRef<ParticipantProtocol.ParticipantMessage> session : participants) {
|
||||||
|
|
||||||
|
// Pícaro
|
||||||
|
if (session.path().name().contains("participante-0")) {
|
||||||
|
chatRoom.tell(new PotRoomProtocol.PlayTurn(session, 0));
|
||||||
|
// Justiciero
|
||||||
|
} else if (session.path().name().contains("participante-1")) {
|
||||||
|
|
||||||
|
// ActorRef<Commands.RoomCommand> chatRoom, double pot,
|
||||||
|
// ActorRef<Events.SessionEvent> replyTo)
|
||||||
|
|
||||||
|
chatRoom.tell(new PotRoomProtocol.PlayTurn(session, 1));
|
||||||
|
// Santo
|
||||||
|
} else if (session.path().name().contains("participante-2")) {
|
||||||
|
chatRoom.tell(new PotRoomProtocol.PlayTurn(session, 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return Behaviors.receive(Void.class)
|
||||||
|
.onSignal(Terminated.class, sig -> Behaviors.stopped())
|
||||||
|
.build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #actor-system
|
||||||
|
}
|
@ -0,0 +1,219 @@
|
|||||||
|
package dev.freireservices.social_altruism.chat.participant;
|
||||||
|
|
||||||
|
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.*;
|
||||||
|
import dev.freireservices.social_altruism.chat.potroom.PotRoomProtocol.PotRoomMessage;
|
||||||
|
import dev.freireservices.social_altruism.chat.potroom.SessionProtocol;
|
||||||
|
import dev.freireservices.social_altruism.chat.potroom.SessionProtocol.SessionMessage;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static dev.freireservices.social_altruism.chat.participant.ParticipantType.JUSTICIERO;
|
||||||
|
import static dev.freireservices.social_altruism.chat.participant.ParticipantType.SANTO;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
public class Participant {
|
||||||
|
|
||||||
|
private final ActorContext<ParticipantMessage> context;
|
||||||
|
private ActorRef<PotRoomMessage> chatRoom;
|
||||||
|
private ActorRef<SessionMessage> session;
|
||||||
|
|
||||||
|
private boolean collaborateSwitch;
|
||||||
|
private int currentTurn;
|
||||||
|
private double participantCoins;
|
||||||
|
private final double initialCoins;
|
||||||
|
private List<ActorRef<ParticipantMessage>> participants;
|
||||||
|
private int totalTurns;
|
||||||
|
private final ParticipantType participantType;
|
||||||
|
|
||||||
|
private Participant(
|
||||||
|
ActorContext<ParticipantMessage> context,
|
||||||
|
double participantCoins,
|
||||||
|
ParticipantType participantType) {
|
||||||
|
this.context = context;
|
||||||
|
this.participantCoins = participantCoins;
|
||||||
|
this.initialCoins = participantCoins;
|
||||||
|
this.participantType = participantType;
|
||||||
|
this.collaborateSwitch = participantType == JUSTICIERO || participantType == SANTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Behavior<ParticipantMessage> create(
|
||||||
|
int initialCoins, ParticipantType participantType) {
|
||||||
|
return Behaviors.setup(ctx -> new Participant(ctx, initialCoins, participantType).behavior());
|
||||||
|
}
|
||||||
|
|
||||||
|
public double decrementCoins(double coins) {
|
||||||
|
this.participantCoins -= coins;
|
||||||
|
|
||||||
|
return coins;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementCoins(double coins) {
|
||||||
|
this.participantCoins += coins;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Behavior<ParticipantMessage> behavior() {
|
||||||
|
return uninitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Behavior<ParticipantMessage> uninitialized() {
|
||||||
|
return Behaviors.receive(ParticipantMessage.class)
|
||||||
|
.onMessage(SessionDenied.class, this::onSessionDenied)
|
||||||
|
.onMessage(SessionGranted.class, x ->
|
||||||
|
{
|
||||||
|
onSessionGranted(x);
|
||||||
|
return readyToStart();
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Behavior<ParticipantMessage> readyToStart() {
|
||||||
|
return Behaviors.receive(ParticipantMessage.class)
|
||||||
|
.onMessage(SessionStarted.class,
|
||||||
|
x -> {
|
||||||
|
onSessionStarted(x);
|
||||||
|
return started();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Behavior<ParticipantMessage> started() {
|
||||||
|
return Behaviors.receive(ParticipantMessage.class)
|
||||||
|
.onMessage(PotReturned.class, this::onPotReturned)
|
||||||
|
.onMessage(SessionEnded.class, this::onSessionEnded)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Behavior<ParticipantMessage> onSessionEnded(SessionEnded sessionEnded) {
|
||||||
|
|
||||||
|
context.getLog().info("Session ended for user: Stats: {}. Earned {} coins, profit {} %"
|
||||||
|
, context.getSelf().path().name()
|
||||||
|
, String.format("%.3f%n", getParticipantCoins() - getInitialCoins())
|
||||||
|
, calculateProfit()
|
||||||
|
);
|
||||||
|
return Behaviors.stopped();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Behavior<ParticipantMessage> onSessionDenied(
|
||||||
|
SessionDenied message) {
|
||||||
|
context.getLog().info("cannot start chat room session: {}", message.reason());
|
||||||
|
return Behaviors.stopped();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSessionGranted(
|
||||||
|
SessionGranted message) {
|
||||||
|
context.getLog().info("Session granted message received for {} ", context.getSelf().path().name());
|
||||||
|
setChatRoom(message.chatRoom());
|
||||||
|
setSession(message.session());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSessionStarted(
|
||||||
|
SessionStarted startSession) {
|
||||||
|
context.getLog().info("Session started for {} with {} participants", context.getSelf().path().name(), startSession.participants().size());
|
||||||
|
resetCurrentTurn();
|
||||||
|
setParticipants(startSession.participants());
|
||||||
|
setTotalTurns(startSession.totalTurns());
|
||||||
|
playTurnWithSmallDelay(startSession.replyTo());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playTurnWithSmallDelay(ActorRef<SessionMessage> replyTo) {
|
||||||
|
if (getParticipantCoins() > 0 && getCurrentTurn() < getTotalTurns()) {
|
||||||
|
context.scheduleOnce(Duration.ofMillis(500),
|
||||||
|
replyTo,
|
||||||
|
new SessionProtocol.PlayTurn(
|
||||||
|
replyTo,
|
||||||
|
context.getSelf(),
|
||||||
|
participants,
|
||||||
|
getCurrentTurn(),
|
||||||
|
getParticipationForCurrentTurn())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getParticipationForCurrentTurn() {
|
||||||
|
var currentTurnCoins = getRandomNumberBetween(0, Math.floor(getParticipantCoins()));
|
||||||
|
return isCollaborateSwitch() ? decrementCoins(currentTurnCoins) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getRandomNumberBetween(double min, double max) {
|
||||||
|
SecureRandom secureRandom = new SecureRandom();
|
||||||
|
return Math.round(secureRandom.nextDouble(max - min) + min);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Behavior<ParticipantMessage> onPotReturned(
|
||||||
|
PotReturned potReturned) {
|
||||||
|
context.getLog().info("Pot returned: {} for participant {}", String.format("%.2f", potReturned.returnedAmount()), potReturned.participant().path().name());
|
||||||
|
incrementCoins(potReturned.returnedAmount());
|
||||||
|
incrementCurrentTurn();
|
||||||
|
context
|
||||||
|
.getLog()
|
||||||
|
.info(
|
||||||
|
"Player {} has now {} coins; started with {} for a partial profit of: {} %",
|
||||||
|
potReturned.participant().path().name(),
|
||||||
|
String.format("%.3f", getParticipantCoins()),
|
||||||
|
getInitialCoins(),
|
||||||
|
calculateProfit());
|
||||||
|
|
||||||
|
// Still game?
|
||||||
|
if (getParticipantCoins() > 1) {
|
||||||
|
playTurnWithSmallDelay(potReturned.session());
|
||||||
|
} else {
|
||||||
|
context
|
||||||
|
.getLog()
|
||||||
|
.info(
|
||||||
|
"Player {} has now {} coins; started with {} for a total profit of: {} %",
|
||||||
|
potReturned.participant().path().name(),
|
||||||
|
getParticipantCoins(),
|
||||||
|
getInitialCoins(),
|
||||||
|
calculateProfit());
|
||||||
|
|
||||||
|
context.getLog().info("END GAME");
|
||||||
|
context.getLog().info("---------");
|
||||||
|
context.getLog().info("END GAME");
|
||||||
|
|
||||||
|
context.stop(context.getSelf());
|
||||||
|
Behaviors.stopped();
|
||||||
|
|
||||||
|
}
|
||||||
|
adjustBehaviour(potReturned);
|
||||||
|
return Behaviors.same();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adjustBehaviour(PotReturned potReturned) {
|
||||||
|
|
||||||
|
switch (participantType) {
|
||||||
|
case SANTO:
|
||||||
|
setCollaborateSwitch(true);
|
||||||
|
break;
|
||||||
|
case PICARO:
|
||||||
|
setCollaborateSwitch(false);
|
||||||
|
break;
|
||||||
|
case JUSTICIERO:
|
||||||
|
// Tweak minimum amount to collaborate; average contribution must be at least the same.
|
||||||
|
setCollaborateSwitch((potReturned.returnedAmount() / participants.size() >= getParticipationForCurrentTurn()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetCurrentTurn() {
|
||||||
|
setCurrentTurn(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementCurrentTurn() {
|
||||||
|
setCurrentTurn(++this.currentTurn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double calculateProfit() {
|
||||||
|
return Math.round((getParticipantCoins() * 100) / getInitialCoins() - 100);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package dev.freireservices.social_altruism.chat.participant;
|
||||||
|
|
||||||
|
import akka.actor.typed.ActorRef;
|
||||||
|
import dev.freireservices.social_altruism.chat.potroom.PotRoomProtocol.PotRoomMessage;
|
||||||
|
import dev.freireservices.social_altruism.chat.potroom.SessionProtocol.SessionMessage;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ParticipantProtocol {
|
||||||
|
public interface ParticipantMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Timeout implements ParticipantMessage {
|
||||||
|
INSTANCE
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SessionGranted(
|
||||||
|
ActorRef<PotRoomMessage> chatRoom,
|
||||||
|
ActorRef<SessionMessage> session
|
||||||
|
)
|
||||||
|
implements ParticipantMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SessionStarted(
|
||||||
|
ActorRef<PotRoomMessage> chatRoom,
|
||||||
|
|
||||||
|
ActorRef<SessionMessage> replyTo,
|
||||||
|
List<ActorRef<ParticipantMessage>> participants,
|
||||||
|
int totalTurns)
|
||||||
|
implements ParticipantMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SessionDenied(String reason) implements ParticipantMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SessionEnded() implements ParticipantMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
public record PotReturned(
|
||||||
|
ActorRef<SessionMessage> session,
|
||||||
|
ActorRef<ParticipantMessage> participant,
|
||||||
|
double returnedAmount)
|
||||||
|
implements ParticipantMessage {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package dev.freireservices.social_altruism.chat.participant;
|
||||||
|
|
||||||
|
public enum ParticipantType {
|
||||||
|
SANTO,
|
||||||
|
JUSTICIERO,
|
||||||
|
PICARO,
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
package dev.freireservices.social_altruism.chat.potroom;
|
||||||
|
|
||||||
|
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.potroom.PotRoomProtocol.EnterPot;
|
||||||
|
import dev.freireservices.social_altruism.chat.potroom.PotRoomProtocol.PotRoomMessage;
|
||||||
|
import dev.freireservices.social_altruism.chat.potroom.SessionProtocol.SessionMessage;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.*;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class PotRoom {
|
||||||
|
private final ActorContext<PotRoomMessage> context;
|
||||||
|
private final List<ActorRef<ParticipantMessage>> participants;
|
||||||
|
|
||||||
|
private final int totalTurns;
|
||||||
|
private final int numberOfParticipants;
|
||||||
|
|
||||||
|
private PotRoom(
|
||||||
|
ActorContext<PotRoomMessage> context,
|
||||||
|
int numberOfParticipants,
|
||||||
|
int totalTurns) {
|
||||||
|
this.context = context;
|
||||||
|
this.participants = new ArrayList<>();
|
||||||
|
this.numberOfParticipants = numberOfParticipants;
|
||||||
|
this.totalTurns = totalTurns;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Behavior<PotRoomMessage> onGetPotSession(
|
||||||
|
ActorRef<PotRoomMessage> chatRoom, EnterPot enterPot) {
|
||||||
|
|
||||||
|
validate(enterPot);
|
||||||
|
|
||||||
|
context.getLog().info("Participant joined {} pot", enterPot.replyTo().path().name());
|
||||||
|
|
||||||
|
ActorRef<ParticipantMessage> participant = enterPot.replyTo();
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
|
||||||
|
// Communicate session start and share pot info with all participants
|
||||||
|
//Fishy, probably could spare one..
|
||||||
|
participants.forEach(p -> p.tell(new SessionGranted(chatRoom, session.narrow())));
|
||||||
|
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 participant(s).", numberOfParticipants - participants.size());
|
||||||
|
return Behaviors.same();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validate(EnterPot enterPot) {
|
||||||
|
// Add check session started
|
||||||
|
if (participants.stream()
|
||||||
|
.anyMatch(
|
||||||
|
s ->
|
||||||
|
s.path()
|
||||||
|
.name()
|
||||||
|
.equals(URLEncoder.encode(enterPot.replyTo().path().name(), UTF_8)))) {
|
||||||
|
|
||||||
|
enterPot.replyTo().tell(new SessionDenied("Can only enter a pot once"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Behavior<PotRoomMessage> createPotBehaviour(ActorRef<PotRoomMessage> chatRoom) {
|
||||||
|
return Behaviors.receive(PotRoomMessage.class)
|
||||||
|
.onMessage(EnterPot.class, x -> onGetPotSession(chatRoom, x))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Behavior<PotRoomMessage> create(int numberOfParticipants, int totalTurns) {
|
||||||
|
return Behaviors.setup(
|
||||||
|
ctx -> new PotRoom(ctx, numberOfParticipants, totalTurns)
|
||||||
|
.createPotBehaviour(ctx.getSelf()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package dev.freireservices.social_altruism.chat.potroom;
|
||||||
|
|
||||||
|
import akka.actor.typed.ActorRef;
|
||||||
|
|
||||||
|
import static dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.ParticipantMessage;
|
||||||
|
|
||||||
|
public class PotRoomProtocol {
|
||||||
|
public interface PotRoomMessage {}
|
||||||
|
|
||||||
|
public record EnterPot(ActorRef<ParticipantMessage> replyTo)
|
||||||
|
implements PotRoomMessage {}
|
||||||
|
}
|
@ -0,0 +1,129 @@
|
|||||||
|
package dev.freireservices.social_altruism.chat.potroom;
|
||||||
|
|
||||||
|
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.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.*;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class Session {
|
||||||
|
|
||||||
|
private final ActorContext<SessionMessage> context;
|
||||||
|
private int currentTurn = 0;
|
||||||
|
private final int totalTurns;
|
||||||
|
private final List<ActorRef<ParticipantMessage>> participants;
|
||||||
|
|
||||||
|
private double currentPot = 0.0;
|
||||||
|
private int numberOfParticipantsInCurrentTurn;
|
||||||
|
|
||||||
|
|
||||||
|
public Session(ActorContext<SessionMessage> context, List<ActorRef<ParticipantMessage>> participants, int totalTurns) {
|
||||||
|
this.context = context;
|
||||||
|
this.participants = participants;
|
||||||
|
this.totalTurns = totalTurns;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void resetPot() {
|
||||||
|
this.currentPot = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addToPot(double pot) {
|
||||||
|
currentPot += pot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementParticipantsInTurn() {
|
||||||
|
this.numberOfParticipantsInCurrentTurn++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetNumberOfParticipantsInTurn() {
|
||||||
|
this.numberOfParticipantsInCurrentTurn = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int incrementCurrentTurnAndGet() {
|
||||||
|
return ++this.currentTurn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Behavior<SessionMessage> createSessionBehaviour() {
|
||||||
|
return Behaviors.receive(SessionMessage.class)
|
||||||
|
.onMessage(StartSession.class, startSession -> onSessionStarted(startSession.chatRoom()
|
||||||
|
, startSession.replyTo(), startSession.participants(), totalTurns))
|
||||||
|
.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(List<ActorRef<ParticipantMessage>> participants, int totalTurns) {
|
||||||
|
return Behaviors.setup(context -> new Session(context, participants, totalTurns)
|
||||||
|
.createSessionBehaviour());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Behavior<SessionMessage> onPlayTurn(
|
||||||
|
PlayTurn playTurn) {
|
||||||
|
context.getLog()
|
||||||
|
.info("Participant {} joined for turn {} with {}",
|
||||||
|
playTurn.replyTo().path().name(),
|
||||||
|
playTurn.turn(),
|
||||||
|
playTurn.pot());
|
||||||
|
|
||||||
|
// Add to current pot
|
||||||
|
addToPot(playTurn.pot());
|
||||||
|
incrementParticipantsInTurn();
|
||||||
|
|
||||||
|
if (getNumberOfParticipantsInCurrentTurn() == participants.size()) {
|
||||||
|
|
||||||
|
double amountToShare = (getCurrentPot() * 2) / participants.size();
|
||||||
|
|
||||||
|
playTurn.session().narrow().tell(new ShareReturnPotWithParticipants(playTurn.session(), playTurn.participants(), amountToShare));
|
||||||
|
|
||||||
|
resetPot();
|
||||||
|
resetNumberOfParticipantsInTurn();
|
||||||
|
|
||||||
|
context.getLog().info("Turn {} complete", getCurrentTurn());
|
||||||
|
|
||||||
|
if (incrementCurrentTurnAndGet() == totalTurns) {
|
||||||
|
context.getLog().info("All turns completed - Waiting for other messages, then ending session.");
|
||||||
|
context.scheduleOnce(Duration.ofSeconds(3), playTurn.session().narrow(), 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 SessionStarted(chatRoom, session, participants, totalTurns)));
|
||||||
|
return Behaviors.same();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Behavior<SessionMessage> onReturnPotToParticipants(
|
||||||
|
ActorRef<SessionMessage> session,
|
||||||
|
List<ActorRef<ParticipantMessage>> participants,
|
||||||
|
double returnedAmount) {
|
||||||
|
participants.forEach(participant -> participant.tell(new PotReturned(session, participant, returnedAmount)));
|
||||||
|
return Behaviors.same();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package dev.freireservices.social_altruism.chat.potroom;
|
||||||
|
|
||||||
|
import akka.actor.typed.ActorRef;
|
||||||
|
import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.ParticipantMessage;
|
||||||
|
import dev.freireservices.social_altruism.chat.potroom.PotRoomProtocol.PotRoomMessage;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class SessionProtocol {
|
||||||
|
|
||||||
|
public interface SessionMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
public record StartSession(
|
||||||
|
ActorRef<PotRoomMessage> chatRoom,
|
||||||
|
ActorRef<SessionMessage> replyTo,
|
||||||
|
List<ActorRef<ParticipantMessage>> participants)
|
||||||
|
implements SessionMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public record PlayTurn(
|
||||||
|
ActorRef<SessionMessage> session,
|
||||||
|
ActorRef<ParticipantMessage> replyTo,
|
||||||
|
List<ActorRef<ParticipantMessage>> participants,
|
||||||
|
int turn,
|
||||||
|
double pot)
|
||||||
|
implements SessionMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
public record ShareReturnPotWithParticipants(
|
||||||
|
ActorRef<SessionMessage> session,
|
||||||
|
List<ActorRef<ParticipantMessage>> participants,
|
||||||
|
double returnedAmount) implements SessionMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public record EndSession() implements SessionMessage { }
|
||||||
|
|
||||||
|
}
|
18
src/main/resources/log4j.xml
Normal file
18
src/main/resources/log4j.xml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
||||||
|
<log4j:configuration debug="true"
|
||||||
|
xmlns:log4j='http://jakarta.apache.org/log4j/'>
|
||||||
|
|
||||||
|
<appender name="console" class="org.apache.log4j.ConsoleAppender">
|
||||||
|
<layout class="org.apache.log4j.PatternLayout">
|
||||||
|
<param name="ConversionPattern"
|
||||||
|
value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
|
||||||
|
</layout>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root>
|
||||||
|
<level value="DEBUG" />
|
||||||
|
<appender-ref ref="console" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</log4j:configuration>
|
@ -0,0 +1,57 @@
|
|||||||
|
package dev.freireservices.social_altruism.chat;
|
||||||
|
|
||||||
|
import akka.actor.testkit.typed.javadsl.ActorTestKit;
|
||||||
|
import akka.actor.typed.ActorRef;
|
||||||
|
import dev.freireservices.social_altruism.chat.participant.Participant;
|
||||||
|
import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.ParticipantMessage;
|
||||||
|
import dev.freireservices.social_altruism.chat.potroom.PotRoom;
|
||||||
|
import dev.freireservices.social_altruism.chat.potroom.PotRoomProtocol;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static dev.freireservices.social_altruism.chat.participant.ParticipantType.*;
|
||||||
|
|
||||||
|
public class CaseStudiesTests {
|
||||||
|
public static final int INITIAL_COINS = 100;
|
||||||
|
public static final int TOTAL_PARTICIPANTS = 3;
|
||||||
|
public static final int TOTAL_TURNS = 100;
|
||||||
|
|
||||||
|
final static ActorTestKit testKit = ActorTestKit.create();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCooperation() {
|
||||||
|
|
||||||
|
var potRoom = PotRoom.create(TOTAL_PARTICIPANTS, TOTAL_TURNS);
|
||||||
|
|
||||||
|
ActorRef<PotRoomProtocol.PotRoomMessage> chatRoomTest =
|
||||||
|
testKit.spawn(potRoom, "potRoom");
|
||||||
|
|
||||||
|
ActorRef<ParticipantMessage> p1 =
|
||||||
|
testKit.spawn(Participant.create(INITIAL_COINS, PICARO), "PICARO-1");
|
||||||
|
|
||||||
|
ActorRef<ParticipantMessage> p2 =
|
||||||
|
testKit.spawn(Participant.create(INITIAL_COINS, JUSTICIERO), "JUSTICIERO-1");
|
||||||
|
|
||||||
|
ActorRef<ParticipantMessage> p3 =
|
||||||
|
testKit.spawn(Participant.create(INITIAL_COINS, SANTO), "SANTO-1");
|
||||||
|
|
||||||
|
// Enter POT
|
||||||
|
chatRoomTest.tell(new PotRoomProtocol.EnterPot(p1));
|
||||||
|
chatRoomTest.tell(new PotRoomProtocol.EnterPot(p2));
|
||||||
|
chatRoomTest.tell(new PotRoomProtocol.EnterPot(p3));
|
||||||
|
|
||||||
|
try {
|
||||||
|
TimeUnit.MINUTES.sleep(1);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void cleanup() {
|
||||||
|
testKit.shutdownTestKit();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
package dev.freireservices.social_altruism.chat;
|
||||||
|
|
||||||
|
import akka.actor.testkit.typed.javadsl.ActorTestKit;
|
||||||
|
import akka.actor.testkit.typed.javadsl.BehaviorTestKit;
|
||||||
|
import akka.actor.testkit.typed.javadsl.TestProbe;
|
||||||
|
import akka.actor.typed.ActorRef;
|
||||||
|
import dev.freireservices.social_altruism.chat.participant.Participant;
|
||||||
|
import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol;
|
||||||
|
import dev.freireservices.social_altruism.chat.participant.ParticipantProtocol.ParticipantMessage;
|
||||||
|
import dev.freireservices.social_altruism.chat.potroom.PotRoom;
|
||||||
|
import dev.freireservices.social_altruism.chat.potroom.PotRoomProtocol;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import static dev.freireservices.social_altruism.chat.participant.ParticipantType.*;
|
||||||
|
|
||||||
|
public class ProtocolTests {
|
||||||
|
public static final int INITIAL_COINS = 100;
|
||||||
|
public static final int TOTAL_PARTICIPANTS = 4;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSessionStartedOnJoinParticipants() {
|
||||||
|
|
||||||
|
final ActorTestKit testKit = ActorTestKit.create();
|
||||||
|
|
||||||
|
TestProbe<ParticipantMessage> testProbe =
|
||||||
|
testKit.createTestProbe("TestProbe", ParticipantMessage.class);
|
||||||
|
|
||||||
|
var potRoom = PotRoom.create(TOTAL_PARTICIPANTS, 1);
|
||||||
|
ActorRef<PotRoomProtocol.PotRoomMessage> chatRoomTest =
|
||||||
|
testKit.spawn(potRoom, "potRoom");
|
||||||
|
|
||||||
|
BehaviorTestKit<PotRoomProtocol.PotRoomMessage> test = BehaviorTestKit.create(potRoom);
|
||||||
|
|
||||||
|
ActorRef<ParticipantMessage> p1 =
|
||||||
|
testKit.spawn(Participant.create(INITIAL_COINS, PICARO), "PICARO-1");
|
||||||
|
|
||||||
|
ActorRef<ParticipantMessage> p2 =
|
||||||
|
testKit.spawn(Participant.create(INITIAL_COINS, JUSTICIERO), "JUSTICIERO-1");
|
||||||
|
|
||||||
|
ActorRef<ParticipantMessage> p3 =
|
||||||
|
testKit.spawn(Participant.create(INITIAL_COINS, SANTO), "SANTO-1");
|
||||||
|
|
||||||
|
// 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(testProbe.ref()));
|
||||||
|
|
||||||
|
//Session started
|
||||||
|
|
||||||
|
testProbe.expectMessageClass(ParticipantProtocol.SessionGranted.class, Duration.ofSeconds(20));
|
||||||
|
testProbe.expectMessageClass(ParticipantProtocol.SessionStarted.class, Duration.ofSeconds(20));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActorGetsSessionGranted() {
|
||||||
|
final ActorTestKit testKit = ActorTestKit.create();
|
||||||
|
TestProbe<ParticipantMessage> testProbe =
|
||||||
|
testKit.createTestProbe("TestProbe");
|
||||||
|
|
||||||
|
ActorRef<PotRoomProtocol.PotRoomMessage> chatRoomTest =
|
||||||
|
testKit.spawn(PotRoom.create(1, 1), "chatRoom");
|
||||||
|
|
||||||
|
chatRoomTest.tell(new PotRoomProtocol.EnterPot(testProbe.ref()));
|
||||||
|
|
||||||
|
testProbe.expectMessageClass(ParticipantProtocol.SessionGranted.class, Duration.ofSeconds(10));
|
||||||
|
|
||||||
|
// #assert
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActorGetsSessionDenied() {
|
||||||
|
final ActorTestKit testKit = ActorTestKit.create();
|
||||||
|
TestProbe<ParticipantMessage> testProbe =
|
||||||
|
testKit.createTestProbe("TestProbe");
|
||||||
|
|
||||||
|
ActorRef<PotRoomProtocol.PotRoomMessage> chatRoomTest =
|
||||||
|
testKit.spawn(PotRoom.create(2, 1), "chatRoom");
|
||||||
|
|
||||||
|
chatRoomTest.tell(new PotRoomProtocol.EnterPot(testProbe.ref()));
|
||||||
|
chatRoomTest.tell(new PotRoomProtocol.EnterPot(testProbe.ref()));
|
||||||
|
|
||||||
|
testProbe.expectMessageClass(ParticipantProtocol.SessionDenied.class, Duration.ofSeconds(5));
|
||||||
|
|
||||||
|
// #assert
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleSessions() {
|
||||||
|
final ActorTestKit testKit = ActorTestKit.create();
|
||||||
|
TestProbe<ParticipantMessage> testProbe =
|
||||||
|
testKit.createTestProbe("TestProbe");
|
||||||
|
|
||||||
|
ActorRef<PotRoomProtocol.PotRoomMessage> chatRoomTest =
|
||||||
|
testKit.spawn(PotRoom.create(2, 1), "chatRoom");
|
||||||
|
|
||||||
|
chatRoomTest.tell(new PotRoomProtocol.EnterPot(testProbe.ref()));
|
||||||
|
chatRoomTest.tell(new PotRoomProtocol.EnterPot(testProbe.ref()));
|
||||||
|
|
||||||
|
testProbe.expectMessageClass(ParticipantProtocol.SessionDenied.class, Duration.ofSeconds(5));
|
||||||
|
|
||||||
|
// #assert
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user